package application

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
	"shimmer/neusoft_club_backend/controllers/cons"
	"shimmer/neusoft_club_backend/controllers/utils"
	"shimmer/neusoft_club_backend/dao/domain"
	"shimmer/neusoft_club_backend/middlewares/auth_middleware"
	"shimmer/neusoft_club_backend/services"
	"shimmer/neusoft_club_backend/services/club_service"
	"shimmer/neusoft_club_backend/services/errs"
	"shimmer/neusoft_club_backend/services/message_service"
)

type updateApplicationStatusFlowMutation struct {
	StatusFlowID int64 `json:"status_flow_id" query:"status_flow_id" form:"status_flow_id" binding:"required"`
}
type findAllApplicationQuery struct {
	StatusFlowID *int64 `json:"status_flow_id" query:"status_flow_id" form:"status_flow_id" binding:"-"`
}

// @Summary 查找社团所有的申请
// @Description
// @Tags 社团
// @Param clubID path integer true "社团ID"
// @Param page query integer true "页面"
// @Param size query integer true "页面大小"
// @Param status_flow_id query integer false "状态ID"
// @Success 200 {object} []domain.UserApplicationDTO
// @Router /api/club/{clubID}/application [get]
func findAllApplicationFromClub(c *gin.Context) {
	user, err := domain.GetUserFromContext(c)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}

	clubBinding := &cons.ClubUriParamBinding{}

	if err = c.ShouldBindUri(clubBinding); err != nil {
		services.AbortWithError(c, err)
		return
	}
	findAllQuery := &findAllApplicationQuery{}

	if err = c.ShouldBindQuery(findAllQuery); err != nil {
		services.AbortWithError(c, err)
		return
	}
	owner, err := club_service.CheckIsClubOwner(user.ID, clubBinding.ClubID)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	if !owner {
		services.AbortWithError(c, errs.ErrNotClubManager)
		return
	}
	pageQuery, err := utils.GetPaginationFromContext(c)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	if findAllQuery.StatusFlowID == nil {
		club, err := club_service.FindAllApplicationForClub(clubBinding.ClubID, pageQuery)
		if err != nil {
			services.AbortWithError(c, err)
			return
		}
		c.JSON(http.StatusOK, club)
		return
	}
	club, err := club_service.FindAllApplicationFromClubByStatus(clubBinding.ClubID, *findAllQuery.StatusFlowID, pageQuery)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	c.JSON(http.StatusOK, club)
	return
}

// @Summary 通过申请
// @Description
// @Tags 社团
// @Param clubID path  integer true "社团ID"
// @Param applicationID path integer true "申请ID"
// @Param accept body club_service.AcceptApplicationMutationDTO true "accept"
// @Success 204
// @Router /api/club/{clubID}/application/{applicationID}/accept [post]
func acceptApplication(c *gin.Context) {
	user, err := domain.GetUserFromContext(c)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	clubBinding := &cons.ClubUriParamBinding{}
	if err = c.ShouldBindUri(clubBinding); err != nil {
		services.AbortWithError(c, err)
		return
	}
	applicationBinding := &cons.ApplicationUriParamBinding{}
	if err = c.ShouldBindUri(applicationBinding); err != nil {
		services.AbortWithError(c, err)
		return
	}
	acceptApplicationMutation := &club_service.AcceptApplicationMutation{}
	if err = c.ShouldBind(acceptApplicationMutation); err != nil {
		services.AbortWithError(c, err)
		return
	}
	if owner, err := club_service.CheckIsClubOwner(user.ID, clubBinding.ClubID); err != nil || !owner {
		if err != nil {
			services.AbortWithError(c, err)
			return
		}
		if !owner {
			services.AbortWithError(c, errs.ErrNotClubManager)
			return
		}
	}
	application, err := club_service.FindUsersApplicationByID(applicationBinding.ApplicationID)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	err = club_service.AcceptApplication(applicationBinding.ApplicationID, *application.FormID, clubBinding.ClubID, acceptApplicationMutation)
	if err != nil {
		services.AbortWithError(c, err)
	}
	_, _ = message_service.SendMessage(&message_service.SendMessageReq{
		Title:      cons.ApplicationStatusChangeMessageTitle,
		Content:    cons.ApplicationStatusChangeMessageContent,
		ReceiverID: &application.UserID,
		SenderID:   nil,
		ClubID:     nil,
		Type:       domain.MessageTypeApplication,
		Extra: &map[string]interface{}{
			"application_id": application.ID,
			"form_id":        application.FormID,
		},
	})
	c.AbortWithStatus(http.StatusNoContent)
}

// @Summary 通过纳新申请
// @Description
// @Tags 社团
// @Param clubID path  integer true "社团ID"
// @Param applicationID path integer true "申请ID"
// @Param accept body club_service.AcceptApplicationMutationDTO true "accept"
// @Success 204
// @Router /api/club/{clubID}/application/{applicationID}/join/accept [post]
func acceptJoinApplication(c *gin.Context) {
	user, err := domain.GetUserFromContext(c)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	clubBinding := &cons.ClubUriParamBinding{}
	if err = c.ShouldBindUri(clubBinding); err != nil {
		services.AbortWithError(c, err)
		return
	}
	applicationBinding := &cons.ApplicationUriParamBinding{}
	if err = c.ShouldBindUri(applicationBinding); err != nil {
		services.AbortWithError(c, err)
		return
	}
	acceptApplicationMutation := &club_service.AcceptApplicationMutation{}
	if err = c.ShouldBind(acceptApplicationMutation); err != nil {
		services.AbortWithError(c, err)
		return
	}
	if owner, err := club_service.CheckIsClubOwner(user.ID, clubBinding.ClubID); err != nil || !owner {
		if err != nil {
			services.AbortWithError(c, err)
			return
		}
		if !owner {
			services.AbortWithError(c, errs.ErrNotClubManager)
			return
		}
	}
	application, err := club_service.FindUsersApplicationByID(applicationBinding.ApplicationID)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	if application.Form.Type != domain.ClubFormTypeFresh {
		services.AbortWithError(c, errs.ErrNotFreshApplication)
		return
	}
	err = club_service.AcceptApplication(applicationBinding.ApplicationID, *application.FormID, clubBinding.ClubID, acceptApplicationMutation)
	if err != nil {
		services.AbortWithError(c, err)
	}
	_, _ = message_service.SendMessage(&message_service.SendMessageReq{
		Title:      cons.ApplicationStatusChangeMessageTitle,
		Content:    cons.ApplicationStatusChangeMessageContent,
		ReceiverID: &application.UserID,
		SenderID:   nil,
		ClubID:     nil,
		Type:       domain.MessageTypeApplication,
		Extra: &map[string]interface{}{
			"application_id": application.ID,
			"form_id":        application.FormID,
		},
	})
	c.AbortWithStatus(http.StatusNoContent)
}

// @Summary 拒绝申请
// @Description
// @Tags 社团
// @Param clubID path  integer true "社团ID"
// @Param applicationID path  integer true "申请ID"
// @Param reject body club_service.RejectApplicationMutationDTO true "reject"
// @Success 204
// @Router /api/club/{clubID}/application/{applicationID}/reject [post]
func rejectApplication(c *gin.Context) {

	user, err := domain.GetUserFromContext(c)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	clubBinding := &cons.ClubUriParamBinding{}
	if err = c.ShouldBindUri(clubBinding); err != nil {
		services.AbortWithError(c, err)
		return
	}
	applicationBinding := &cons.ApplicationUriParamBinding{}
	if err = c.ShouldBindUri(applicationBinding); err != nil {
		services.AbortWithError(c, err)
		return
	}
	rejectApplicationMutation := &club_service.RejectApplicationMutation{}
	if err = c.ShouldBind(rejectApplicationMutation); err != nil {
		services.AbortWithError(c, err)
		return
	}
	if owner, err := club_service.CheckIsClubOwner(user.ID, clubBinding.ClubID); err != nil || !owner {
		if err != nil {
			services.AbortWithError(c, err)
			return
		}
		if !owner {
			services.AbortWithError(c, errs.ErrNotClubManager)
			return
		}
	}
	application, err := club_service.FindUsersApplicationByID(applicationBinding.ApplicationID)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	err = club_service.RejectApplication(applicationBinding.ApplicationID, *application.FormID, clubBinding.ClubID, rejectApplicationMutation)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	_, _ = message_service.SendMessage(&message_service.SendMessageReq{
		Title:      cons.ApplicationStatusChangeMessageTitle,
		Content:    cons.ApplicationStatusChangeMessageContent,
		ReceiverID: &application.UserID,
		SenderID:   nil,
		ClubID:     nil,
		Type:       domain.MessageTypeApplication,
		Extra: &map[string]interface{}{
			"application_id": application.ID,
			"form_id":        application.FormID,
		},
	})
	c.AbortWithStatus(http.StatusNoContent)
}

// @Summary 拒绝纳新申请
// @Description
// @Tags 社团
// @Param clubID path  integer true "社团ID"
// @Param applicationID path integer true "申请ID"
// @Param reject body club_service.RejectApplicationMutationDTO true "reject"
// @Success 204
// @Router /api/club/{clubID}/application/{applicationID}/join/reject [post]
func rejectJoinApplication(c *gin.Context) {
	user, err := domain.GetUserFromContext(c)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	clubBinding := &cons.ClubUriParamBinding{}
	if err = c.ShouldBindUri(clubBinding); err != nil {
		services.AbortWithError(c, err)
		return
	}
	applicationBinding := &cons.ApplicationUriParamBinding{}
	if err = c.ShouldBindUri(applicationBinding); err != nil {
		services.AbortWithError(c, err)
		return
	}
	rejectApplicationMutation := &club_service.RejectApplicationMutation{}
	if err = c.ShouldBind(rejectApplicationMutation); err != nil {
		services.AbortWithError(c, err)
		return
	}
	if owner, err := club_service.CheckIsClubOwner(user.ID, clubBinding.ClubID); err != nil || !owner {
		if err != nil {
			services.AbortWithError(c, err)
			return
		}
		if !owner {
			services.AbortWithError(c, errs.ErrNotClubManager)
			return
		}
	}
	application, err := club_service.FindUsersApplicationByID(applicationBinding.ApplicationID)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	if application.Form.Type != domain.ClubFormTypeFresh {
		services.AbortWithError(c, errs.ErrNotFreshApplication)
		return
	}
	err = club_service.RejectApplication(applicationBinding.ApplicationID, *application.FormID, clubBinding.ClubID, rejectApplicationMutation)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	_, _ = message_service.SendMessage(&message_service.SendMessageReq{
		Title:      cons.ApplicationStatusChangeMessageTitle,
		Content:    cons.ApplicationStatusChangeMessageContent,
		ReceiverID: &application.UserID,
		SenderID:   nil,
		ClubID:     nil,
		Type:       domain.MessageTypeApplication,
		Extra: &map[string]interface{}{
			"application_id": application.ID,
			"form_id":        application.FormID,
		},
	})
	c.AbortWithStatus(http.StatusNoContent)
}

// @Summary 更新申请状态
// @Description
// @Tags 社团
// @Param clubID path  integer true "社团ID"
// @Param applicationID path integer true "申请ID"
// @Param update query application.updateApplicationStatusFlowMutation true "update"
// @Success 200 {object} domain.ClubFormStatusFlowDTO
// @Router /api/club/{clubID}/application/{applicationID}/flow [post]
func updateApplicationStatusFlow(c *gin.Context) {
	user, err := domain.GetUserFromContext(c)

	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	clubBinding := &cons.ClubUriParamBinding{}
	if err = c.ShouldBindUri(clubBinding); err != nil {
		services.AbortWithError(c, err)
		return
	}
	applicationBinding := &cons.ApplicationUriParamBinding{}
	if err = c.ShouldBindUri(applicationBinding); err != nil {
		services.AbortWithError(c, err)
		return
	}
	updateStatus := &updateApplicationStatusFlowMutation{}
	if err = c.ShouldBindQuery(updateStatus); err != nil {
		services.AbortWithError(c, err)
		return
	}

	owner, err := club_service.CheckIsClubOwner(user.ID, clubBinding.ClubID)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	if !owner {
		services.AbortWithError(c, errs.ErrNotClubManager)
		return
	}
	flow, err := club_service.FindClubFormStatusFlow(updateStatus.StatusFlowID)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	if flow.ClubID == nil {
		services.AbortWithError(c, errs.ErrFormStatusIsBroken)
		return
	}
	if *flow.ClubID != clubBinding.ClubID {
		services.AbortWithError(c, errs.ErrFormStatusNotInClub)
		return
	}
	application, err := club_service.FindUsersApplicationByID(applicationBinding.ApplicationID)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	err = club_service.UpdateApplicationStatusFlow(applicationBinding.ApplicationID, updateStatus.StatusFlowID)
	_, _ = message_service.SendMessage(&message_service.SendMessageReq{
		Title:      cons.ApplicationStatusChangeMessageTitle,
		Content:    cons.ApplicationStatusChangeMessageContent,
		ReceiverID: &application.UserID,
		SenderID:   nil,
		ClubID:     nil,
		Type:       domain.MessageTypeApplication,
		Extra: &map[string]interface{}{
			"application_id": application.ID,
			"form_id":        application.FormID,
		},
	})
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	c.JSON(http.StatusOK, flow)
}

// @Summary 重置申请状态
// @Description 调用此方法会重置申请状态到草稿，用户可以重写和再次公开自己的申请
// @Tags 社团
// @Param clubID path integer true "社团ID"
// @Param applicationID path integer true "申请ID"
// @Success 200 {object} domain.ClubFormStatusFlowDTO
// @Router /api/club/{clubID}/application/{applicationID}/reset [post]
func resetApplicationStatusFlow(c *gin.Context) {
	user, err := domain.GetUserFromContext(c)

	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	clubBinding := &cons.ClubUriParamBinding{}
	if err = c.ShouldBindUri(clubBinding); err != nil {
		services.AbortWithError(c, err)
		return
	}
	applicationBinding := &cons.ApplicationUriParamBinding{}
	if err = c.ShouldBindUri(applicationBinding); err != nil {
		services.AbortWithError(c, err)
		return
	}
	owner, err := club_service.CheckIsClubOwner(user.ID, clubBinding.ClubID)
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	if !owner {
		services.AbortWithError(c, errs.ErrNotClubManager)
		return
	}
	status, err := club_service.FindDraftFormStatus()
	if err != nil {
		services.AbortWithError(c, err)
		return
	}
	if err = club_service.UpdateApplicationStatusFlow(applicationBinding.ApplicationID, status.ID); err != nil {
		services.AbortWithError(c, err)
		return
	}
	c.JSON(http.StatusOK, status)
}

func ConfigureClubUserApplicationController(r *gin.RouterGroup) {
	r.GET("/", auth_middleware.AuthLoginMiddleware(), findAllApplicationFromClub)
	r.POST(fmt.Sprintf("/:%s/accept", cons.ApplicationUriParam), auth_middleware.AuthLoginMiddleware(), acceptApplication)
	r.POST(fmt.Sprintf("/:%s/reject", cons.ApplicationUriParam), auth_middleware.AuthLoginMiddleware(), rejectApplication)
	r.POST(fmt.Sprintf("/:%s/join/accept", cons.ApplicationUriParam), auth_middleware.AuthLoginMiddleware(), acceptJoinApplication)
	r.POST(fmt.Sprintf("/:%s/join/reject", cons.ApplicationUriParam), auth_middleware.AuthLoginMiddleware(), rejectJoinApplication)
	r.POST(fmt.Sprintf("/:%s/flow", cons.ApplicationUriParam), auth_middleware.AuthLoginMiddleware(), updateApplicationStatusFlow)
	r.POST(fmt.Sprintf("/:%s/rest", cons.ApplicationUriParam), auth_middleware.AuthLoginMiddleware(), resetApplicationStatusFlow)
	r.POST(fmt.Sprintf("/:%s/reset", cons.ApplicationUriParam), auth_middleware.AuthLoginMiddleware(), resetApplicationStatusFlow)

}
